package com.cloudlead.common.worker.impl;

import com.cloudlead.common.lang.*;
import com.cloudlead.common.lang.annotation.*;
import com.cloudlead.common.lang.api.ActionRequest;
import com.cloudlead.common.lang.api.ActionResponse;
import com.cloudlead.common.lang.api.SimpleActionRequest;
import com.cloudlead.common.lang.service.DictDataService;
import com.cloudlead.common.worker.ApiRegistry;
import com.cloudlead.common.worker.action.Action;
import com.cloudlead.common.worker.action.ActionFactory;
import com.cloudlead.common.worker.action.impl.DefaultSimpleAction;
import com.cloudlead.common.worker.annotation.Order;
import com.cloudlead.common.worker.annotation.ValidatorGroup;
import com.cloudlead.common.worker.desc.ActionDesc;
import com.cloudlead.common.worker.desc.ModuleDesc;
import com.cloudlead.common.worker.desc.datatype.*;
import com.cloudlead.common.worker.desc.security.RequireGroupsDesc;
import com.cloudlead.common.worker.desc.security.RequirePrivilegesDesc;
import com.cloudlead.common.worker.desc.security.RequireRolesDesc;
import com.cloudlead.common.worker.desc.security.SecurityDesc;
import com.cloudlead.common.worker.security.annotation.RequireGroups;
import com.cloudlead.common.worker.security.annotation.RequireGuest;
import com.cloudlead.common.worker.security.annotation.RequirePrivileges;
import com.cloudlead.common.worker.security.annotation.RequireRoles;
import com.cloudlead.common.worker.utils.MethodParamsNameHelper;
import com.cloudlead.common.worker.validator.EmailValidator;
import com.cloudlead.common.worker.validator.PatternValidator;
import com.cloudlead.common.worker.validator.SizeValidator;
import com.cloudlead.common.worker.validator.Validator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.Email;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.Map.Entry;

@Component
public class DefaultApiRegistry implements ApiRegistry {

    public static final String REQUEST_CONTENT_PARAM = "content";
    @Resource
    ApplicationContext ctx;

    @Autowired(required = false)
    DictDataService dictDataService;

    private Map<String, ModuleDesc> allModules = new LinkedHashMap<>();

    private Map<String, Map<String, ActionDesc>> allActions = new LinkedHashMap<>();

    private Map<String, Module> allModule = new LinkedHashMap<>();

    @SuppressWarnings("rawtypes")
    private Map<String, Action> allAction = new LinkedHashMap<>();

    private Map<Type, TypeDesc> cachedTypeDesc = new LinkedHashMap<>();

    private Map<String, FormData> types = new LinkedHashMap<>();

    @PostConstruct
    public void start() {
        try {
            Map<Class<?>, ActionFactory> allActionFactory = new LinkedHashMap<>();
            Map<String, ActionFactory> beansOfType = ctx.getBeansOfType(ActionFactory.class);
            Collection<ActionFactory> values = beansOfType.values();
            for (ActionFactory actionFactory : values) {
                allActionFactory.put(actionFactory.getClass(), actionFactory);
            }

            MapData simpleRequest = parseComplexDataType(SimpleActionRequest.class, null, null);


            for (Entry<Class<?>, ActionFactory> entry : allActionFactory.entrySet()) {
                final Class<?> actionFactoryClass = entry.getKey();
                ActionFactory actionFactory = entry.getValue();

                BusinessModule module = actionFactory.getModule();
                if (null == module) {
                    module = new BusinessModule() {

                        @Override
                        public BusinessModule getParent() {
                            return null;
                        }

                        @Override
                        public String getName() {
                            return actionFactoryClass.getSimpleName();
                        }

                        @Override
                        public String getLabel() {
                            Label label = actionFactoryClass.getAnnotation(Label.class);
                            if (null != label) {
                                return label.value();
                            }
                            return getName();
                        }

                        @Override
                        public String getFullName() {
                            return actionFactoryClass.getSimpleName();
                        }

                        @Override
                        public int getOrdinal() {
                            Order order = actionFactoryClass.getAnnotation(Order.class);
                            if (null != order) {
                                return order.value();
                            }
                            return 0;
                        }
                    };
                }
                ModuleDesc moduleDesc = findModule(module, actionFactoryClass);

                allModule.put(moduleDesc.getFullName(), actionFactory);
                allModules.put(moduleDesc.getFullName(), moduleDesc);

                Type superType = actionFactoryClass.getGenericSuperclass();
                if (superType instanceof ParameterizedType) {
                    ParameterizedType ptype = (ParameterizedType) superType;
                    Type cmpType = ptype.getActualTypeArguments()[1];
                    Class<?> dataClass = (Class<?>) cmpType;
                    FormData data = parseDataType(dataClass, null, null, null);
                    moduleDesc.setData(data);
                }

                Map<String, ActionDesc> map = new LinkedHashMap<>();
                allActions.put(moduleDesc.getFullName(), map);

                List<ActionDesc> actionDescs = new ArrayList<>();

                Method[] methods = actionFactoryClass.getDeclaredMethods();
                for (int j = 0; j < methods.length; j++) {
                    Method method = methods[j];
                    if (method.isBridge()) {
                        continue;
                    }
                    String[] methodParamNames = MethodParamsNameHelper.getMethodParamNames(method);
                    ActionDesc api = new ActionDesc();

                    api.setModule(moduleDesc);

                    String simpleActionName = method.getName();
                    com.cloudlead.common.worker.annotation.Action actionAnnotation = method.getAnnotation(com.cloudlead.common.worker.annotation.Action.class);
                    if (null == actionAnnotation) {
                        continue;
                    }
                    if (StringUtils.isNotBlank(actionAnnotation.value())) {
                        simpleActionName = actionAnnotation.value();
                    }

                    api.setName(moduleDesc.getFullName() + "@" + simpleActionName);
                    api.setComment(method.getName());
                    Label actionLabelAnnotation = method.getAnnotation(Label.class);
                    if (null != actionLabelAnnotation) {
                        api.setComment(actionLabelAnnotation.value());
                    }

                    DefaultSimpleAction action = new DefaultSimpleAction(allActionFactory.get(actionFactoryClass), method);
                    allAction.put(api.getName(), action);
                    api.setType(action.getClass());

                    ValidatorGroup validatorGroup = method.getAnnotation(ValidatorGroup.class);
                    Class<?>[] group = new Class<?>[]{};
                    if (null != validatorGroup) {
                        List<Class<?>> list = new ArrayList<>();
                        list.addAll(Arrays.asList(validatorGroup.value()));
                        group = list.toArray(new Class<?>[list.size()]);
                    }
                    Parameter[] parameters = method.getParameters();

                    simpleRequest = (MapData) simpleRequest.clone();
                    Map<String, FormData> mapping = simpleRequest.getMapping();

                    MapData contentMap = new MapData();
                    contentMap.setLabel("content");
                    contentMap.setDesc(Messages.getMessage("worker.api.request.desc"));
                    contentMap.setRequired(true);
                    Map<String, FormData> contentMapping = new HashMap<>();
                    contentMap.setMapping(contentMapping);
                    mapping.put(REQUEST_CONTENT_PARAM, contentMap);

                    boolean hasUserInfo = false;
                    for (int i = 0; i < parameters.length; i++) {
                        String paramName = methodParamNames[i];
                        if (null == paramName) {
                            paramName = parameters[i].getName();
                        }
                        Class<?> parameterType = parameters[i].getType();
                        Type parameterizedType = parameters[i].getParameterizedType();

                        TypeDesc typeDesc = parseTypeDesc(parameterizedType);
                        List<TypeDesc> elementTypes = typeDesc.getElementType();
                        Class<?> elementType = null;
                        if (null != elementTypes && elementTypes.size() > 0) {
                            elementType = elementTypes.get(0).getRawType();
                        }
                        FormData request = parseDataType(parameterType, elementType, null, group);
                        try {
                            if (null != request) {
                                request = (FormData) request.clone();
                            }
                        } catch (CloneNotSupportedException e) {
                            e.printStackTrace();
                        }
                        if (null != request) {
                            simpleRequest.getJavaType().addElementType(request.getJavaType());
                            Label lableAnno = parameters[i].getAnnotation(Label.class);
                            if (null != lableAnno) {
                                request.setDesc(lableAnno.value());
                            }

                            NotNull notNullAnno = parameters[i].getAnnotation(NotNull.class);
                            if (null != notNullAnno) {
                                request.setRequired(true);
                            }

                            InputFormat inputFormat = parameters[i].getAnnotation(InputFormat.class);
                            if (null != inputFormat && request instanceof FormData) {
                                FormData simpleData = (FormData) request;
                                simpleData.setFormat(inputFormat.value().name());
                            }

                            contentMapping.put(paramName, request);
                        }

                    }

                    api.setRequest(simpleRequest);

                    Class<?> returnType = method.getReturnType();
                    FormData response = parseDataType(returnType, null, null, null);
                    api.setResponse(response);

                    Order orderAnnotation = method.getAnnotation(Order.class);
                    if (null != orderAnnotation) {
                        api.setOrdinal(orderAnnotation.value());
                    }

                    SecurityDesc securityDesc = new SecurityDesc();
                    securityDesc.setRequireUserInfo(hasUserInfo);
                    RequireGuest requireGuest = actionFactoryClass.getAnnotation(RequireGuest.class);
                    if (null != requireGuest) {
                        securityDesc.setRequireGuest(true);
                    }
                    RequirePrivileges requirePrivileges = actionFactoryClass.getAnnotation(RequirePrivileges.class);
                    if (null != requirePrivileges) {
                        securityDesc.addRequirePrivilegesDesc(new RequirePrivilegesDesc(requirePrivileges.value(), requirePrivileges.logical()));
                    }
                    RequireRoles requireRoles = actionFactoryClass.getAnnotation(RequireRoles.class);
                    if (null != requireRoles) {
                        securityDesc.addRequireRolesDesc(new RequireRolesDesc(requireRoles.value(), requireRoles.logical()));
                    }
                    RequireGroups requireGroups = actionFactoryClass.getAnnotation(RequireGroups.class);
                    if (null != requireGroups) {
                        securityDesc.addRequireGroupsDesc(new RequireGroupsDesc(requireGroups.value(), requireGroups.logical()));
                    }

                    RequireGuest requireGuest1 = method.getAnnotation(RequireGuest.class);
                    if (null != requireGuest1) {
                        securityDesc.setRequireGuest(true);
                    }
                    RequirePrivileges requirePrivileges1 = method.getAnnotation(RequirePrivileges.class);
                    if (null != requirePrivileges1) {
                        securityDesc.addRequirePrivilegesDesc(new RequirePrivilegesDesc(requirePrivileges1.value(), requirePrivileges1.logical()));
                    }
                    RequireRoles requireRoles1 = method.getAnnotation(RequireRoles.class);
                    if (null != requireRoles1) {
                        securityDesc.addRequireRolesDesc(new RequireRolesDesc(requireRoles1.value(), requireRoles1.logical()));
                    }
                    RequireGroups requireGroups1 = method.getAnnotation(RequireGroups.class);
                    if (null != requireGroups1) {
                        securityDesc.addRequireGroupsDesc(new RequireGroupsDesc(requireGroups1.value(), requireGroups1.logical()));
                    }

                    api.setSecurityDesc(securityDesc);

                    actionDescs.add(api);
                }
                Collections.sort(actionDescs);
                for (ActionDesc actionDesc : actionDescs) {
                    map.put(actionDesc.getName(), actionDesc);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    private ModuleDesc findModule(BusinessModule module, Class<?> actionFactoryClass) {
        ModuleDesc moduleDesc = new ModuleDesc();
        moduleDesc.setName(module.getName());
        moduleDesc.setComment(module.getLabel());
        moduleDesc.setOrdinal(module.getOrdinal());
        Type[] interfaces = actionFactoryClass.getGenericInterfaces();
        for (Type type : interfaces) {
            moduleDesc.addModuleFacade(type.getTypeName());
        }
        if (null != module.getParent()) {
            Module parentModule = new SimpleModule(module.getParent());
            allModule.put(module.getParent().getFullName(), parentModule);
            moduleDesc.setParent(findModule(module.getParent(), actionFactoryClass));
        }
        return moduleDesc;
    }

    @Override
    public Map<String, ActionDesc> getAllApiDesc() {
        Map<String, ActionDesc> allActions = new LinkedHashMap<>();
        for (Map<String, ActionDesc> all : this.allActions.values()) {
            allActions.putAll(all);
        }
        return allActions;
    }

    @Override
    public ActionDesc getApiDesc(String actionName) {
        return getAllApiDesc().get(actionName);
    }

    @SuppressWarnings("unchecked")
    @Override
    public <Reqest extends ActionRequest, Response extends ActionResponse, A extends Action<Reqest, Response>> A get(String actionName) {
        return (A) allAction.get(actionName);
    }

    private TypeDesc parseTypeDesc(Type type) {
        TypeDesc typeDesc = cachedTypeDesc.get(type);
        if (null != typeDesc) {
            return typeDesc;
        }
        TypeDesc responseElementtype = new TypeDesc();
        responseElementtype.setType(type);
        if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            responseElementtype.setRawType((Class<?>) pt.getRawType());
            Type[] actualTypeArguments = pt.getActualTypeArguments();
            for (Type typeArg : actualTypeArguments) {
                responseElementtype.addElementType(parseTypeDesc(typeArg));
            }
        } else {
            Class<?> cls = (Class<?>) type;
            responseElementtype.setRawType(cls);
        }

        Class<?> rawType = responseElementtype.getRawType();
        if (isPrimitive(rawType)) {
            responseElementtype.setPrimitiveType(true);
        }
        if (Collection.class.isAssignableFrom(rawType)) {
            responseElementtype.setCollectionType(true);
            if (null == responseElementtype.getElementType() || responseElementtype.getElementType().isEmpty()) {
                Class<?> resolve = ResolvableType.forClass(rawType).asCollection().getGeneric(0).resolve();
                if (null == resolve) {
                    resolve = Object.class;
                }
                responseElementtype.addElementType(parseTypeDesc(resolve));
            }
        }
        if (rawType.isArray()) {
            if (null == responseElementtype.getElementType() || responseElementtype.getElementType().isEmpty()) {
                Class<?> resolve = rawType.getComponentType();
                if (null == resolve) {
                    resolve = Object.class;
                }
                responseElementtype.addElementType(parseTypeDesc(resolve));
            }
        }
        if (rawType.isEnum()) {
            responseElementtype.setEnumType(true);
        }
        cachedTypeDesc.put(type, typeDesc);
        return responseElementtype;
    }

    private boolean isPrimitive(Class<?> type) {
        return type.isPrimitive() || Long.class.equals(type) || Integer.class.equals(type) || Short.class.equals(type) || Float.class.equals(type) || Double.class.equals(type)
                || BigDecimal.class.equals(type) || Date.class.equals(type) || Timestamp.class.equals(type) || LocalDate.class.equals(type) || DateTime.class.equals(type) || Boolean.class.equals(type) || String.class.equals(type);
    }

    private MapData parseComplexDataType(Class<?> javaType, Class<?> parentType, Class<?>[] group) {
        try {
            if (null == javaType.getDeclaredConstructor()) {
                return null;
            }
        } catch (NoSuchMethodException | SecurityException e) {
            return null;
        }
        MapData formData = new MapData();
        formData.setJavaType(parseTypeDesc(javaType));
        Label typeComment = javaType.getAnnotation(Label.class);
        formData.setLabel(javaType.getSimpleName());
        if (null != typeComment) {
            formData.setDesc(typeComment.value());
        }

        HidePropertys hidePropertyAnos = javaType.getAnnotation(HidePropertys.class);
        HideProperty[] hidePropertys = null;
        if (null != hidePropertyAnos) {
            hidePropertys = hidePropertyAnos.values();
        } else {
            HideProperty hidePropertyAno = javaType.getAnnotation(HideProperty.class);
            if (null != hidePropertyAno) {
                hidePropertys = new HideProperty[]{hidePropertyAno};
            }
        }

        boolean isCheckHideProperty = false;
        String[] showProperties = null;
        String[] hideProperties = null;
        if (null != hidePropertys) {
            isCheckHideProperty = true;
            List<String> showPropertyList = new ArrayList<>();
            List<String> hidePropertyList = new ArrayList<>();
            for (HideProperty hideProperty : hidePropertys) {
                boolean hasHidePropertyGroup = false;
                Class<?>[] groups = hideProperty.groups();
                if (groups.length == 0) {
                } else if (null != group) {
                    for (Class<?> g : group) {
                        if (containGroup(g, groups)) {
                            hasHidePropertyGroup = true;
                            break;
                        }
                    }
                }
                if (hasHidePropertyGroup) {
                    String[] showProperties_ = hideProperty.showProperties();
                    if (null != showProperties_) {
                        showPropertyList.addAll(Arrays.asList(showProperties_));
                    }
                    String[] hideProperties_ = hideProperty.hideProperties();
                    if (null != hideProperties_) {
                        hidePropertyList.addAll(Arrays.asList(hideProperties_));
                    }

                }
            }

            showProperties = showPropertyList.toArray(new String[showPropertyList.size()]);
            hideProperties = hidePropertyList.toArray(new String[hidePropertyList.size()]);
        }

        Map<String, FormData> properties = new LinkedHashMap<>();
        BeanWrapper beanWrapper = new BeanWrapperImpl(javaType);
        PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            if (!propertyDescriptor.getPropertyType().equals(parentType) && null != propertyDescriptor.getReadMethod() && !Modifier.isFinal(propertyDescriptor.getReadMethod().getModifiers())
                    && null != propertyDescriptor.getWriteMethod()) {
                TypeDescriptor propertyTypeDescriptor = beanWrapper.getPropertyTypeDescriptor(propertyDescriptor.getName());

                JsonIgnore jsonIgnore = propertyTypeDescriptor.getAnnotation(JsonIgnore.class);
                if (null != jsonIgnore) {
                    continue;
                }

                if (isCheckHideProperty
                        && (hideProperties.length > 0 && hasThisProperty(propertyDescriptor.getName(), hideProperties) || showProperties.length > 0
                        && !hasThisProperty(propertyDescriptor.getName(), showProperties))) {
                    continue;
                }

                TypeDescriptor elementTypeDescriptor = propertyTypeDescriptor.getElementTypeDescriptor();
                FormData property = parseDataType(propertyTypeDescriptor.getType(), null == elementTypeDescriptor ? null : elementTypeDescriptor.getType(), javaType, group);
                try {
                    if (null != property) {
                        property = (FormData) property.clone();
                    }
                } catch (CloneNotSupportedException e) {
                    e.printStackTrace();
                }
                if (null == property) {
                    continue;
                }
                property.setName(propertyDescriptor.getName());
                property.setLabel(propertyDescriptor.getName());
                Label comment = propertyTypeDescriptor.getAnnotation(Label.class);
                if (null != comment) {
                    property.setDesc(comment.value());
                }

                DictValue dictValue = propertyTypeDescriptor.getAnnotation(DictValue.class);
                if (null != dictValue && null != dictDataService) {
                    if (property instanceof FormData) {
                        FormData simpleData = (FormData) property;
                        List<? extends DictData> dictList = dictDataService.findDicts(dictValue.group());
                        simpleData.setDict(dictList);
                        simpleData.setDictGroup(dictValue.group());
                    }
                }

                InputFormat inputFormat = propertyTypeDescriptor.getAnnotation(InputFormat.class);
                if (null != inputFormat && property instanceof FormData) {
                    FormData simpleData = (FormData) property;
                    simpleData.setFormat(inputFormat.value().name());
                }

                NotNull notNull = propertyTypeDescriptor.getAnnotation(NotNull.class);
                if (null != notNull) {
                    Class<?>[] groups = notNull.groups();
                    if (groups.length == 0) {
                        property.setRequired(true);
                    } else if (null != group) {
                        for (Class<?> g : group) {
                            if (containGroup(g, groups)) {
                                property.setRequired(true);
                            }
                        }
                    }
                }

                List<Validator> validators = new ArrayList<>();

                Size size = propertyTypeDescriptor.getAnnotation(Size.class);
                if (null != size) {
                    Class<?>[] groups = size.groups();
                    if (groups.length == 0) {
                        SizeValidator sizeValidator = new SizeValidator(size.min(), size.max(), size.message(), size.groups(), size.payload());
                        validators.add(sizeValidator);
                    } else if (null != group) {
                        for (Class<?> g : group) {
                            if (containGroup(g, groups)) {
                                SizeValidator sizeValidator = new SizeValidator(size.min(), size.max(), size.message(), size.groups(), size.payload());
                                validators.add(sizeValidator);
                            }
                        }
                    }
                }

                Pattern pattern = propertyTypeDescriptor.getAnnotation(Pattern.class);
                if (null != pattern) {
                    Class<?>[] groups = pattern.groups();
                    if (groups.length == 0) {
                        String regexp = pattern.regexp();
                        PatternValidator patternValidator = new PatternValidator(regexp, pattern.flags(), pattern.message(), pattern.groups(), pattern.payload());
                        validators.add(patternValidator);
                    } else if (null != group) {
                        for (Class<?> g : group) {
                            if (containGroup(g, groups)) {
                                String regexp = pattern.regexp();
                                PatternValidator patternValidator = new PatternValidator(regexp, pattern.flags(), pattern.message(), pattern.groups(), pattern.payload());
                                validators.add(patternValidator);
                            }
                        }
                    }
                }

                Email email = propertyTypeDescriptor.getAnnotation(Email.class);
                if (null != email) {
                    Class<?>[] groups = email.groups();
                    if (groups.length == 0) {
                        EmailValidator emailValidator = new EmailValidator(email.regexp(), email.flags(), email.message(), email.groups(), email.payload());
                        validators.add(emailValidator);
                    } else if (null != group) {
                        for (Class<?> g : group) {
                            if (containGroup(g, groups)) {
                                EmailValidator emailValidator = new EmailValidator(email.regexp(), email.flags(), email.message(), email.groups(), email.payload());
                                validators.add(emailValidator);
                            }
                        }
                    }
                }

                if (!validators.isEmpty()) {
                    property.setValidators(validators);
                }
                properties.put(propertyDescriptor.getName(), property);
            }
        }
        formData.setMapping(properties);
        return formData;
    }

    private boolean hasThisProperty(String prop, String[] showProperties) {
        if (null != showProperties) {
            for (String prop_ : showProperties) {
                if (prop_.equals(prop)) {
                    return true;
                }
            }
        }
        return false;
    }

    private boolean containGroup(Class<?> group, Class<?>[] groups) {
        for (int i = 0; i < groups.length; i++) {
            if (group.equals(groups[i])) {
                return true;
            }
        }
        return false;
    }

    private FormData parseDataType(Class<?> type, Class<?> elementType, Class<?> parentType, Class<?>[] group) {
        String dataTypeName = toString(type) + "_" + toString(elementType) + "_";
        if (null != group) {
            for (int i = 0; i < group.length; i++) {
                dataTypeName += group[i].getName() + ",";
            }
        }
        dataTypeName = DigestUtils.md5Hex(dataTypeName);
        FormData formData = types.get(dataTypeName);
        if (null != formData) {
            return formData;
        }

        TypeDesc typeDesc = parseTypeDesc(type);
        if (null != elementType) {
            List<TypeDesc> elementTypes = new ArrayList<>();
            elementTypes.add(parseTypeDesc(elementType));
            typeDesc.setElementType(elementTypes);
        }

        FormData property = null;

        if (typeDesc.isCollectionType() || type.isArray()) {
            SeqData seqData = new SeqData();

            List<FormData> sequence = new ArrayList<>();
            FormData child = parseDataType(typeDesc.getElementType().get(0).getRawType(), null, parentType, group);
            if (null != child) {
                sequence.add(child);
                seqData.setSequence(sequence);
                property = seqData;
            }
        } else if (typeDesc.isPrimitiveType()) {
            if (Integer.class.equals(type) || Short.class.equals(type) || Long.class.equals(type) || int.class.equals(type) || short.class.equals(type) || long.class.equals(type)) {
                property = new IntData();
            } else if (Number.class.isAssignableFrom(type) || float.class.equals(type) || double.class.equals(type)) {
                property = new NumberData();
            } else if (Boolean.class.equals(type) || boolean.class.equals(type)) {
                property = new BoolData();
            } else if (Date.class.equals(type) || LocalDate.class.equals(type)) {
                StrData strData = new StrData();
                strData.setFormat(FormatPattern.date.name());
                property = strData;
            } else if (Timestamp.class.equals(type) || DateTime.class.equals(type)) {
                StrData strData = new StrData();
                strData.setFormat(FormatPattern.datetime.name());
                property = strData;
            } else {
                property = new StrData();
            }
        } else if (Object.class.equals(type)) {
            property = new AnyData();
        } else if (type.isEnum()) {
            IntData intData = new IntData();
            List<DictData> dictData = new ArrayList<>();

            Field[] declaredFields = type.getDeclaredFields();
            for (Field field : declaredFields) {
                if (DictData.class.equals(field.getType())) {
                    Enum valueOf = Enum.valueOf((Class<Enum>) type, field.getName());
                    dictData.add((DictData) valueOf);
                } else if (type.equals(field.getType())) {
                    @SuppressWarnings({"unchecked", "rawtypes"})
                    Enum valueOf = Enum.valueOf((Class<Enum>) type, field.getName());
                    String enumLabel = valueOf.name();
                    Label fieldComment = field.getAnnotation(Label.class);
                    if (null != fieldComment) {
                        enumLabel = fieldComment.value();
                    }
                    dictData.add(new SimpleDictData(valueOf.ordinal(), enumLabel));
                }
            }
            intData.setDict(dictData);
            property = intData;
        } else if (!Modifier.isAbstract(type.getModifiers()) && !Modifier.isInterface(type.getModifiers())) {
            property = parseComplexDataType(type, parentType, group);
        }
        if (null != property) {
            property.setJavaType(typeDesc);
            property.setLabel(type.getSimpleName());

            Label labelAnnotation = type.getAnnotation(Label.class);
            if (null != labelAnnotation) {
                property.setDesc(labelAnnotation.value());
            }

        }
        types.put(dataTypeName, property);
        return property;
    }

    private String toString(Class<?> type) {
        return null == type ? "" : type.getName();
    }

    @Override
    public Map<String, ModuleDesc> getAllModuleDesc() {
        return allModules;
    }

    @Override
    public Map<String, Module> getAllModule() {
        return allModule;
    }

    @Override
    public Map<String, ActionDesc> getApiDescs(String module) {
        return allActions.get(module);
    }

    @Override
    public void enableModule(String moduleName) {
        handleModule(moduleName, true);
    }

    private void handleModule(String moduleName, boolean enabled) {
        handleSingleModule(moduleName, enabled);
        List<String> childModules = new ArrayList<>();
        for (Entry<String, ModuleDesc> entry : allModules.entrySet()) {
            String key = entry.getKey();
            ModuleDesc value = entry.getValue();
            if (hasModule(value, moduleName)) {
                childModules.add(key);
            }
        }

        for (String childMuduleName : childModules) {
            handleSingleModule(childMuduleName, enabled);
        }
    }

    private void handleSingleModule(String moduleName, boolean enabled) {
        Module module = allModule.get(moduleName);
        if (null != module) {
            if (enabled) {
                module.enable();
            } else {
                module.disable();
            }
        }
    }

    private boolean hasModule(ModuleDesc value, String moduleName) {
        if (null != value.getParent()) {
            ModuleDesc parent = value.getParent();
            if (moduleName.equals(parent.getFullName())) {
                return true;
            } else {
                return hasModule(parent, moduleName);
            }
        }
        return false;
    }

    @Override
    public void disableModule(String moduleName) {
        handleModule(moduleName, false);
    }
}
